/*
 * Copyright (c) 2014, Mentor Graphics Corporation
 * All rights reserved.
 * Copyright (c) 2015 Xilinx, Inc. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <metal/alloc.h>
#include <metal/log.h>
#include <metal/utilities.h>
#include <openamp/elf_loader.h>
#include <openamp/remoteproc.h>
#include <openamp/remoteproc_loader.h>
#include <openamp/remoteproc_virtio.h>
#include <openamp/rsc_table_parser.h>

/******************************************************************************
 *  static functions
 *****************************************************************************/
static const struct loader_ops * remoteproc_check_fw_format(const void *img_data, size_t img_len)
{
    if (img_len <= 0) {
        return NULL;
    } else if (elf_identify(img_data, img_len) == 0) {
        return &elf_ops;
    } else {
        return NULL;
    }
}

/* 根据指定内存名称或物理地址或设备地址或虚拟地址, 获取添加到Remoteproc内部列表中的内部块. */
static struct remoteproc_mem * remoteproc_get_mem(struct remoteproc *rproc,
    const char *name, metal_phys_addr_t pa, metal_phys_addr_t da,
    void *va, size_t size, struct remoteproc_mem *buf)
{
    struct metal_list *node;
    struct remoteproc_mem *mem;

    /* 检查名称长度以避免溢出. 该测试必须保持符合MISRA. */
    if (name && strlen(name) > RPROC_MAX_NAME_LEN) {
        return NULL;
    }

    metal_list_for_each(&rproc->mems, node) {
        mem = metal_container_of(node, struct remoteproc_mem, node);
        if (name) {
            if (!strncmp(name, mem->name, RPROC_MAX_NAME_LEN)) {
                return mem;
            }
        } else if (pa != METAL_BAD_PHYS) {
            metal_phys_addr_t pa_start, pa_end;

            pa_start = mem->pa;
            pa_end = pa_start + mem->size;
            if (pa >= pa_start && (pa + size) <= pa_end && pa < pa_end) {
                return mem;
            }
        } else if (da != METAL_BAD_PHYS) {
            metal_phys_addr_t da_start, da_end;

            da_start = mem->da;
            da_end = da_start + mem->size;
            if (da >= da_start && (da + size) <= da_end && da < da_end) {
                return mem;
            }
        } else if (va) {
            if (metal_io_virt_to_offset(mem->io, va) != METAL_BAD_OFFSET) {
                return mem;
            }
        } else {
            return NULL;
        }
    }

    if (!rproc->ops->get_mem) {
        return NULL;
    }

    return rproc->ops->get_mem(rproc, name, pa, da, va, size, buf);
}

static metal_phys_addr_t remoteproc_datopa(struct remoteproc_mem *mem, metal_phys_addr_t da)
{
    metal_phys_addr_t pa;

    pa = mem->pa + da - mem->da;
    return pa;
}

static metal_phys_addr_t remoteproc_patoda(struct remoteproc_mem *mem, metal_phys_addr_t pa)
{
    metal_phys_addr_t da;

    da = mem->da + pa - mem->pa;
    return da;
}

/* 从ELF镜像中复制资源表到本地内存, 并检查资源的合法性 */
static void *remoteproc_get_rsc_table(struct remoteproc *rproc, void *store,
    const struct image_store_ops *store_ops, size_t offset, size_t len)
{
    void *rsc_table = NULL;
    const void *img_data;
    int ret;

    /* 将资源表复制到本地内存, 调用方应负责释放内存. */
    rsc_table = metal_allocate_memory(len);
    if (rsc_table == NULL) {
        return RPROC_ERR_PTR(-RPROC_ENOMEM);
    }

    /* 从ELF镜像装载资源表 */
    ret = store_ops->load(store, offset, len, &img_data, RPROC_LOAD_ANYADDR, NULL, 1);
    if (ret < 0 || ret < (int)len || !img_data) {
        metal_log(METAL_LOG_ERROR, "get rsc failed: 0x%llx, 0x%llx\r\n", offset, len);
        ret = -RPROC_EINVAL;
        goto error;
    }

    /* 复制资源表到本地内存 */
    memcpy(rsc_table, img_data, len);

    /* 检查资源的合法性, 同时给虚拟设备分配通知ID */
    ret = handle_rsc_table(rproc, rsc_table, len, NULL);
    if (ret < 0) {
        goto error;
    }
    return rsc_table;

error:
    metal_free_memory(rsc_table);
    return RPROC_ERR_PTR(ret);
}

static int remoteproc_parse_rsc_table(struct remoteproc *rproc,
    struct resource_table *rsc_table, size_t rsc_size)
{
    struct metal_io_region *io;

    if (!rsc_table) {
        return -RPROC_EINVAL;
    }

    io = remoteproc_get_io_with_va(rproc, rsc_table);
    /* 检查资源的合法性, 同时给虚拟设备分配通知ID */
    return handle_rsc_table(rproc, rsc_table, rsc_size, io);
}

int remoteproc_set_rsc_table(struct remoteproc *rproc,
    struct resource_table *rsc_table, size_t rsc_size)
{
    int ret;
    struct metal_io_region *io;

    if (!rproc || !rsc_table || rsc_size == 0) {
        return -RPROC_EINVAL;
    }

    io = remoteproc_get_io_with_va(rproc, rsc_table);
    if (!io) {
        return -RPROC_EINVAL;
    }

    ret = remoteproc_parse_rsc_table(rproc, rsc_table, rsc_size);
    if (!ret) {
        rproc->rsc_table = rsc_table;
        rproc->rsc_len = rsc_size;
        rproc->rsc_io = io;
    }

    return ret;
}

struct remoteproc *remoteproc_init(struct remoteproc *rproc,
                   const struct remoteproc_ops *ops, void *priv)
{
    if (!rproc || !ops) {
        return NULL;
    }

    memset(rproc, 0, sizeof(*rproc));
    rproc->state = RPROC_OFFLINE;
    metal_mutex_init(&rproc->lock);
    metal_list_init(&rproc->mems);
    metal_list_init(&rproc->vdevs);
    rproc = ops->init(rproc, ops, priv);
    return rproc;
}

int remoteproc_remove(struct remoteproc *rproc)
{
    int ret = 0;

    if (!rproc)
        return -RPROC_EINVAL;

    metal_mutex_acquire(&rproc->lock);
    if (rproc->state == RPROC_OFFLINE) {
        if (rproc->ops->remove)
            rproc->ops->remove(rproc);
    } else {
        ret = -RPROC_EAGAIN;
    }
    metal_mutex_release(&rproc->lock);
    return ret;
}

int remoteproc_config(struct remoteproc *rproc, void *data)
{
    int ret = -RPROC_ENODEV;

    if (rproc) {
        metal_mutex_acquire(&rproc->lock);
        if (rproc->state == RPROC_OFFLINE) {
            /*
             * 如果状态为掉电时, 则允许配置操作.
             * 这个函数可以在远程启动前多次调用.
             */
            if (rproc->ops->config) {
                ret = rproc->ops->config(rproc, data);
            } else {
                ret = 0;
            }
            rproc->state = RPROC_READY;
        } else {
            ret = -RPROC_EINVAL;
        }
        metal_mutex_release(&rproc->lock);
    }
    return ret;
}

int remoteproc_start(struct remoteproc *rproc)
{
    int ret = -RPROC_ENODEV;

    if (rproc) {
        metal_mutex_acquire(&rproc->lock);
        if (rproc->state == RPROC_READY) {
            ret = rproc->ops->start(rproc);
            rproc->state = RPROC_RUNNING;
        } else {
            ret = -RPROC_EINVAL;
        }
        metal_mutex_release(&rproc->lock);
    }
    return ret;
}

int remoteproc_stop(struct remoteproc *rproc)
{
    int ret = -RPROC_ENODEV;

    if (rproc) {
        metal_mutex_acquire(&rproc->lock);
        if (rproc->state != RPROC_STOPPED &&
            rproc->state != RPROC_OFFLINE) {
            if (rproc->ops->stop)
                ret = rproc->ops->stop(rproc);
            rproc->state = RPROC_STOPPED;
        } else {
            ret = 0;
        }
        metal_mutex_release(&rproc->lock);
    }
    return ret;
}

int remoteproc_shutdown(struct remoteproc *rproc)
{
    int ret = -RPROC_ENODEV;

    if (rproc) {
        ret = 0;
        metal_mutex_acquire(&rproc->lock);
        if (rproc->state != RPROC_OFFLINE) {
            if (rproc->state != RPROC_STOPPED) {
                if (rproc->ops->stop)
                    ret = rproc->ops->stop(rproc);
            }
            if (!ret) {
                if (rproc->ops->shutdown)
                    ret = rproc->ops->shutdown(rproc);
                if (!ret) {
                    rproc->state = RPROC_OFFLINE;
                }
            }
        }
        metal_mutex_release(&rproc->lock);
    }
    return ret;
}

struct metal_io_region *
remoteproc_get_io_with_name(struct remoteproc *rproc,
                const char *name)
{
    struct remoteproc_mem *mem;
    struct remoteproc_mem buf;

    if (!rproc)
        return NULL;

    mem = remoteproc_get_mem(rproc, name,
                 METAL_BAD_PHYS, METAL_BAD_PHYS, NULL, 0, &buf);
    if (mem)
        return mem->io;

    return NULL;
}

struct metal_io_region * remoteproc_get_io_with_pa(struct remoteproc *rproc, metal_phys_addr_t pa)
{
    struct remoteproc_mem *mem;
    struct remoteproc_mem buf;

    if (!rproc) {
        return NULL;
    }

    mem = remoteproc_get_mem(rproc, NULL, pa, METAL_BAD_PHYS, NULL, 0, &buf);
    if (mem) {
        return mem->io;
    }

    return NULL;
}

struct metal_io_region * remoteproc_get_io_with_da(struct remoteproc *rproc, metal_phys_addr_t da, unsigned long *offset)
{
    struct remoteproc_mem *mem;
    struct remoteproc_mem buf;

    if (!rproc || !offset) {
        return NULL;
    }

    mem = remoteproc_get_mem(rproc, NULL, METAL_BAD_PHYS, da, NULL, 0, &buf);
    if (mem) {
        struct metal_io_region *io;
        metal_phys_addr_t pa;

        io = mem->io;
        pa = remoteproc_datopa(mem, da);
        *offset = metal_io_phys_to_offset(io, pa);
        return io;
    }

    return NULL;
}

struct metal_io_region * remoteproc_get_io_with_va(struct remoteproc *rproc, void *va)
{
    struct remoteproc_mem *mem;
    struct remoteproc_mem buf;

    if (!rproc) {
        return NULL;
    }

    mem = remoteproc_get_mem(rproc, NULL, METAL_BAD_PHYS, METAL_BAD_PHYS, va, 0, &buf);
    if (mem) {
        return mem->io;
    }

    return NULL;
}

void *remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, metal_phys_addr_t *da,
                      size_t size, unsigned int attribute, struct metal_io_region **io)
{
    void *va = NULL;
    metal_phys_addr_t lpa, lda;
    struct remoteproc_mem *mem;
    struct remoteproc_mem buf;

    if (!rproc || size == 0 || (!pa && !da)) {
        return NULL;
    }
    if (pa) {
        lpa = *pa;
    } else {
        lpa = METAL_BAD_PHYS;
    }
    if (da) {
        lda =  *da;
    } else {
        lda = METAL_BAD_PHYS;
    }
    mem = remoteproc_get_mem(rproc, NULL, lpa, lda, NULL, size, &buf);
    if (mem) {
        if (lpa != METAL_BAD_PHYS) {
            lda = remoteproc_patoda(mem, lpa);
        } else if (lda != METAL_BAD_PHYS) {
            lpa = remoteproc_datopa(mem, lda);
        }
        if (io) {
            *io = mem->io;
        }
        va = metal_io_phys_to_virt(mem->io, lpa);
    } else if (rproc->ops->mmap) {
        va = rproc->ops->mmap(rproc, &lpa, &lda, size, attribute, io);
    }

    if (pa) {
        *pa = lpa;
    }
    if (da) {
        *da = lda;
    }

    return va;
}

int remoteproc_load(struct remoteproc *rproc, const char *path,
    void *store, const struct image_store_ops *store_ops, void **img_info)
{
    int ret;
    const struct loader_ops *loader;
    const void *img_data;
    void *limg_info = NULL;
    size_t offset, noffset;
    size_t len, nlen;
    int last_load_state;
    metal_phys_addr_t da, rsc_da;
    size_t rsc_size = 0;
    void *rsc_table = NULL;
    struct metal_io_region *io = NULL;

    if (!rproc) {
        return -RPROC_ENODEV;
    }

    metal_mutex_acquire(&rproc->lock);
    metal_log(METAL_LOG_DEBUG, "%s: check remoteproc status, state = %u\r\n", __func__, rproc->state);
    /* 如果remoteproc未处于就绪状态, 则无法加载可执行文件. */
    if (rproc->state != RPROC_READY && rproc->state != RPROC_CONFIGURED) {
        metal_log(METAL_LOG_ERROR, "load failure: invalid rproc state %d.\r\n", rproc->state);
        metal_mutex_release(&rproc->lock);
        return -RPROC_EINVAL;
    }

    if (!store_ops) {
        metal_log(METAL_LOG_ERROR, "load failure: loader ops is not set.\r\n");
        metal_mutex_release(&rproc->lock);
        return -RPROC_EINVAL;
    }

    /* 打开可执行文件以准备解析. */
    metal_log(METAL_LOG_DEBUG, "%s: open executable image, path = %s\r\n", __func__, path);
    ret = store_ops->open(store, path, &img_data);
    if (ret <= 0) {
        metal_log(METAL_LOG_ERROR, "load failure: failed to open firmware %d.\r\n", ret);
        metal_mutex_release(&rproc->lock);
        return -RPROC_EINVAL;
    }
    len = ret;
    metal_assert(img_data);

    /* 检查可执行镜像格式以选择解析器 */
    loader = rproc->loader;
    if (!loader) {
        metal_log(METAL_LOG_DEBUG, "%s: check loader\r\n", __func__);
        loader = remoteproc_check_fw_format(img_data, len);
        if (!loader) {
            metal_log(METAL_LOG_ERROR, "load failure: failed to get store ops.\r\n");
            ret = -RPROC_EINVAL;
            goto error1;
        }
        rproc->loader = loader;
    }

    /* 加载可执行镜像头数据 */
    metal_log(METAL_LOG_DEBUG, "%s: loading headers\r\n", __func__);
    offset = 0;
    last_load_state = RPROC_LOADER_NOT_READY;
    while (1) {
        ret = loader->load_header(img_data, offset, len, &limg_info, last_load_state, &noffset, &nlen);
        last_load_state = ret;
        metal_log(METAL_LOG_DEBUG, "%s, load header 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n",
            __func__, offset, len, noffset, nlen);
        if (ret < 0) {
            metal_log(METAL_LOG_ERROR, "load header failed 0x%lx,%d.\r\n", offset, len);
            goto error2;
        } else if ((ret & RPROC_LOADER_READY_TO_LOAD) != 0) {
            if (nlen == 0) {
                break;
            } else if ((noffset > (offset + len)) && (store_ops->features & SUPPORT_SEEK) == 0) {
                /*
                 * 所需的数据不连续, 且不支持seek, 停止加载镜像头数据, 例如ELF段头, 通常位于镜像的末尾.
                 * 继续将二进制数据加载到目标内存.
                 */
                break;
            }
        }
        /* 继续加载镜像头数据 */
        img_data = NULL;
        ret = store_ops->load(store, noffset, nlen, &img_data, RPROC_LOAD_ANYADDR, NULL, 1);
        if (ret < (int)nlen) {
            metal_log(METAL_LOG_ERROR, "load image data failed 0x%x,%d\r\n", noffset, nlen);
            goto error2;
        }
        offset = noffset;
        len = nlen;
    }
    ret = loader->locate_rsc_table(limg_info, &rsc_da, &offset, &rsc_size);
    if (ret == 0 && rsc_size > 0) {
        /* 从ELF镜像复制资源表到本地内存, 检查资源表合法性, 同时给虚拟设备分配通知ID */
        rsc_table = remoteproc_get_rsc_table(rproc, store, store_ops, offset, rsc_size);
    }

    /* 加载可执行数据 */
    metal_log(METAL_LOG_DEBUG, "%s: load executable data\r\n", __func__);
    offset = 0;
    len = 0;
    while (1) {
        unsigned char padding;
        size_t nmemsize;
        metal_phys_addr_t pa;

        da = RPROC_LOAD_ANYADDR;
        nlen = 0;
        nmemsize = 0;
        noffset = 0;
        ret = loader->load_data(rproc, img_data, offset, len, &limg_info,
            last_load_state, &da, &noffset, &nlen, &padding, &nmemsize);
        if (ret < 0) {
            metal_log(METAL_LOG_ERROR, "load data failed, 0x%lx,%d\r\n", noffset, nlen);
            goto error3;
        }
        metal_log(METAL_LOG_DEBUG, "load data: da 0x%lx, offset 0x%lx, len = 0x%lx,"
            " memsize = 0x%lx, state 0x%x\r\n", da, noffset, nlen, nmemsize, ret);
        last_load_state = ret;
        if (da != RPROC_LOAD_ANYADDR) {
            /* 数据应该被加载到目标内存中 */
            img_data = NULL;
            /* 从remoteproc获取I/O内存区域 */
            pa = METAL_BAD_PHYS;
            (void)remoteproc_mmap(rproc, &pa, &da, nmemsize, 0, &io);
            if (pa == METAL_BAD_PHYS || !io) {
                metal_log(METAL_LOG_ERROR, "load failed, no mapping for 0x%llx.\r\n", da);
                ret = -RPROC_EINVAL;
                goto error3;
            }
            if (nlen > 0) {
                ret = store_ops->load(store, noffset, nlen, &img_data, pa, io, 1);
                if (ret != (int)nlen) {
                    metal_log(METAL_LOG_ERROR, "load data failed 0x%lx, 0x%lx, 0x%x\r\n", pa, noffset, nlen);
                    ret = -RPROC_EINVAL;
                    goto error3;
                }
            }
            if (nmemsize > nlen) {
                size_t tmpoffset = metal_io_phys_to_offset(io, pa + nlen);
                metal_io_block_set(io, tmpoffset, padding, (nmemsize - nlen));
            }
        } else if (nlen != 0) {
            ret = store_ops->load(store, noffset, nlen, &img_data, RPROC_LOAD_ANYADDR, NULL, 1);
            if (ret < (int)nlen) {
                if ((last_load_state & RPROC_LOADER_POST_DATA_LOAD) != 0) {
                    metal_log(METAL_LOG_WARNING, "not all the headers are loaded\r\n");
                    break;
                }
                metal_log(METAL_LOG_ERROR, "post-load image data failed 0x%x,%d\r\n", noffset, nlen);
                goto error3;
            }
            offset = noffset;
            len = nlen;
        } else {
            /* (last_load_state & RPROC_LOADER_LOAD_COMPLETE) != 0 */
            break;
        }
    }

    if (rsc_size == 0) {
        ret = loader->locate_rsc_table(limg_info, &rsc_da, &offset, &rsc_size);
        if (ret == 0 && rsc_size > 0) {
            /* 从ELF镜像复制资源表到本地内存, 检查资源表合法性, 同时给虚拟设备分配通知ID */
            rsc_table = remoteproc_get_rsc_table(rproc, store, store_ops, offset, rsc_size);
        }
    }

    /* 更新资源表 */
    if (rsc_table) {
        void *rsc_table_cp = rsc_table;

        metal_log(METAL_LOG_DEBUG, "%s, update resource table\r\n", __func__);
        rsc_table = remoteproc_mmap(rproc, NULL, &rsc_da, rsc_size, 0, &io);
        if (rsc_table) {
            /* 更新资源表 */
            size_t rsc_io_offset = metal_io_virt_to_offset(io, rsc_table);
            ret = metal_io_block_write(io, rsc_io_offset, rsc_table_cp, rsc_size);
            if (ret != (int)rsc_size) {
                metal_log(METAL_LOG_WARNING, "load: failed to update rsc\r\n");
            }
            rproc->rsc_table = rsc_table;
            rproc->rsc_len = rsc_size;
            rproc->rsc_io = io;
        } else {
            metal_log(METAL_LOG_WARNING, "load: not able to update rsc table.\r\n");
        }
        metal_free_memory(rsc_table_cp);
        /* 这样rsc_table就不会被释放 */
        rsc_table = NULL;
    }

    metal_log(METAL_LOG_DEBUG, "%s: successfully load firmware\r\n", __func__);
    /* 从固件获取入口点 */
    rproc->bootaddr = loader->get_entry(limg_info);
    rproc->state = RPROC_READY;

    metal_mutex_release(&rproc->lock);
    if (img_info) {
        *img_info = limg_info;
    } else {
        loader->release(limg_info);
    }
    store_ops->close(store);
    return 0;

error3:
    if (rsc_table) {
        metal_free_memory(rsc_table);
    }
error2:
    loader->release(limg_info);
error1:
    store_ops->close(store);
    metal_mutex_release(&rproc->lock);
    return ret;
}

int remoteproc_load_noblock(struct remoteproc *rproc, const void *img_data,
    size_t offset, size_t len, void **img_info, metal_phys_addr_t *pa,
    struct metal_io_region **io, size_t *noffset, size_t *nlen,
    size_t *nmlen, unsigned char *padding)
{
    int ret;
    const struct loader_ops *loader;
    void *limg_info = NULL;
    int last_load_state;
    metal_phys_addr_t da, rsc_da;
    size_t rsc_size;
    void *rsc_table = NULL, *lrsc_table = NULL;

    if (!rproc) {
        return -RPROC_ENODEV;
    }

    metal_assert(pa);
    metal_assert(io);
    metal_assert(noffset);
    metal_assert(nlen);
    metal_assert(nmlen);
    metal_assert(padding);

    metal_mutex_acquire(&rproc->lock);
    metal_log(METAL_LOG_DEBUG, "%s: check remoteproc status\r\n", __func__);
    /* 如果remoteproc未处于就绪状态, 则无法加载可执行文件 */
    if (rproc->state != RPROC_READY) {
        metal_log(METAL_LOG_ERROR, "load failure: invalid rproc state %d.\r\n", rproc->state);
        metal_mutex_release(&rproc->lock);
        return -RPROC_EINVAL;
    }

    /* 检查可执行镜像格式以选择解析器 */
    loader = rproc->loader;
    if (!loader) {
        metal_log(METAL_LOG_DEBUG, "%s: check loader\r\n", __func__);
        if (!img_data || offset != 0 || len == 0) {
            metal_log(METAL_LOG_ERROR, "load failure, invalid inputs, not able to identify image.\r\n");
            metal_mutex_release(&rproc->lock);
            return -RPROC_EINVAL;
        }
        loader = remoteproc_check_fw_format(img_data, len);
        if (!loader) {
            metal_log(METAL_LOG_ERROR, "load failure: failed to identify image.\r\n");
            metal_mutex_release(&rproc->lock);
            return -RPROC_EINVAL;
        }
        rproc->loader = loader;
    }

    if (!img_info || !*img_info) {
        last_load_state = 0;
    } else {
        limg_info = *img_info;
        last_load_state = loader->get_load_state(limg_info);
        if (last_load_state < 0) {
            metal_log(METAL_LOG_ERROR, "load failure, not able get load state.\r\n");
            metal_mutex_release(&rproc->lock);
            return -RPROC_EINVAL;
        }
    }
    da = RPROC_LOAD_ANYADDR;
    *nlen = 0;
    if ((last_load_state & RPROC_LOADER_READY_TO_LOAD) == 0 &&
        (last_load_state & RPROC_LOADER_LOAD_COMPLETE) == 0) {
        /* 获取强制执行镜像头 */
        ret = loader->load_header(img_data, offset, len, &limg_info, last_load_state, noffset, nlen);
        metal_log(METAL_LOG_DEBUG, "%s, load header 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n",
            __func__, offset, len, *noffset, *nlen);
        if (ret < 0) {
            metal_log(METAL_LOG_ERROR, "load header failed 0x%lx,%d.\r\n", offset, len);
            goto error1;
        }
        last_load_state = ret;
        if (*nlen != 0 && (last_load_state & RPROC_LOADER_READY_TO_LOAD) == 0) {
            goto out;
        }
    }
    if ((last_load_state & RPROC_LOADER_READY_TO_LOAD) != 0 ||
        (last_load_state & RPROC_LOADER_POST_DATA_LOAD) != 0) {
        /* 足够的信息来知道哪个目标内存对应哪个数据. */
        ret = loader->load_data(rproc, img_data, offset, len,
            &limg_info, last_load_state, &da, noffset, nlen, padding, nmlen);
        metal_log(METAL_LOG_DEBUG, "%s, load data 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n",
              __func__, offset, len, *noffset, *nlen);
        if (ret < 0) {
            metal_log(METAL_LOG_ERROR, "load data failed,0x%lx,%d\r\n", offset, len);
            goto error1;
        }
        last_load_state = ret;
        if (da != RPROC_LOAD_ANYADDR) {
            /* get the I/O region from remoteproc */
            *pa = METAL_BAD_PHYS;
            (void)remoteproc_mmap(rproc, pa, &da, *nmlen, 0, io);
            if (*pa == METAL_BAD_PHYS || !io) {
                metal_log(METAL_LOG_ERROR, "load failed, no mapping for 0x%llx.\r\n", da);
                ret = -RPROC_EINVAL;
                goto error1;
            }
        }
        if (*nlen != 0) {
            goto out;
        }
    }
    if ((last_load_state & RPROC_LOADER_LOAD_COMPLETE) != 0) {
        /* 获取资源表 */
        size_t rsc_offset;
        size_t rsc_io_offset;

        ret = loader->locate_rsc_table(limg_info, &rsc_da, &rsc_offset, &rsc_size);
        if (ret == 0 && rsc_size > 0) {
            lrsc_table = metal_allocate_memory(rsc_size);
            if (!lrsc_table) {
                ret = -RPROC_ENOMEM;
                goto error1;
            }
            rsc_table = remoteproc_mmap(rproc, NULL, &rsc_da, rsc_size, 0, io);
            if (!*io) {
                metal_log(METAL_LOG_ERROR, "load failed: failed to mmap rsc\r\n");
                metal_free_memory(lrsc_table);
                goto error1;
            }
            rsc_io_offset = metal_io_virt_to_offset(*io, rsc_table);
            ret = metal_io_block_read(*io, rsc_io_offset, lrsc_table, rsc_size);
            if (ret != (int)rsc_size) {
                metal_log(METAL_LOG_ERROR, "load failed: failed to get rsc\r\n");
                metal_free_memory(lrsc_table);
                goto error1;
            }
            /* 解析资源表 */
            ret = remoteproc_parse_rsc_table(rproc, lrsc_table, rsc_size);
            if (ret < 0) {
                metal_log(METAL_LOG_ERROR, "load failed: failed to parse rsc\r\n");
                metal_free_memory(lrsc_table);
                goto error1;
            }
            /* 更新资源表 */
            ret = metal_io_block_write(*io, rsc_io_offset, lrsc_table, rsc_size);
            if (ret != (int)rsc_size) {
                metal_log(METAL_LOG_WARNING, "load executable, failed to update rsc\r\n");
            }
            rproc->rsc_table = rsc_table;
            rproc->rsc_len = rsc_size;
            rproc->rsc_io = *io;
            metal_free_memory(lrsc_table);
        }

        /* 从固件获取入口点 */
        rproc->bootaddr = loader->get_entry(limg_info);
    }
out:
    if (img_info) {
        *img_info = limg_info;
    } else {
        loader->release(limg_info);
    }
    metal_mutex_release(&rproc->lock);
    return 0;

error1:
    loader->release(limg_info);
    metal_mutex_release(&rproc->lock);
    return ret;
}

unsigned int remoteproc_allocate_id(struct remoteproc *rproc, unsigned int start, unsigned int end)
{
    unsigned int notifyid = RSC_NOTIFY_ID_ANY;

    if (start == RSC_NOTIFY_ID_ANY) {
        start = 0;
    }
    if (end == RSC_NOTIFY_ID_ANY) {
        end = METAL_BITS_PER_ULONG;
    }
    if ((start < (8U * sizeof(rproc->bitmap))) && (end <= (8U * sizeof(rproc->bitmap)))) {
        notifyid = metal_bitmap_next_clear_bit(&rproc->bitmap, start, end);
        if (notifyid != end) {
            metal_bitmap_set_bit(&rproc->bitmap, notifyid);
        } else {
            notifyid = RSC_NOTIFY_ID_ANY;
        }
    }
    return notifyid;
}

static int remoteproc_virtio_notify(void *priv, uint32_t id)
{
    struct remoteproc *rproc = priv;

    return (rproc->ops->notify) ? rproc->ops->notify(rproc, id) : 0;
}

struct virtio_device * remoteproc_create_virtio(struct remoteproc *rproc,
    int vdev_id, unsigned int role, void (*rst_cb)(struct virtio_device *vdev))
{
    char *rsc_table;
    struct fw_rsc_vdev *vdev_rsc;
    struct metal_io_region *vdev_rsc_io;
    struct virtio_device *vdev;
    struct remoteproc_virtio *rpvdev;
    size_t vdev_rsc_offset;
    unsigned int notifyid;
    unsigned int num_vrings, i;
    struct metal_list *node;

#ifdef VIRTIO_DRIVER_ONLY
    role = (role != VIRTIO_DEV_DRIVER) ? 0xFFFFFFFFUL : role;
#endif

#ifdef VIRTIO_DEVICE_ONLY
    role = (role != VIRTIO_DEV_DEVICE) ? 0xFFFFFFFFUL : role;
#endif

    if (!rproc || (role != VIRTIO_DEV_DEVICE && role != VIRTIO_DEV_DRIVER)) {
        return NULL;
    }

    metal_assert(rproc);
    metal_mutex_acquire(&rproc->lock);
    rsc_table = rproc->rsc_table;
    vdev_rsc_io = rproc->rsc_io;
    vdev_rsc_offset = find_rsc(rsc_table, RSC_VDEV, vdev_id);
    if (!vdev_rsc_offset) {
        metal_log(METAL_LOG_ERROR, "%s: Failed to find vdev (%d) from rsc table.\r\n", __func__, vdev_id);
        metal_mutex_release(&rproc->lock);
        return NULL;
    }
    vdev_rsc = (struct fw_rsc_vdev *)(rsc_table + vdev_rsc_offset);
    notifyid = vdev_rsc->notifyid;
    /* 检查Virtio设备是否已经创建 */
    metal_list_for_each(&rproc->vdevs, node) {
        rpvdev = metal_container_of(node, struct remoteproc_virtio, node);
        if (rpvdev->vdev.notifyid == notifyid) {
            metal_mutex_release(&rproc->lock);
            return &rpvdev->vdev;
        }
    }
    vdev = rproc_virtio_create_vdev(role, notifyid, vdev_rsc, vdev_rsc_io, rproc, remoteproc_virtio_notify, rst_cb);
    if (!vdev) {
        metal_log(METAL_LOG_ERROR, "%s: Failed to create vdev (%d).\r\n", __func__, vdev_id);
        metal_mutex_release(&rproc->lock);
        return NULL;
    }

    rproc_virtio_wait_remote_ready(vdev);

    rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
    metal_list_add_tail(&rproc->vdevs, &rpvdev->node);
    num_vrings = vdev_rsc->num_of_vrings;

    /* 设置Vrings的通知ID */
    for (i = 0; i < num_vrings; i++) {
        struct fw_rsc_vdev_vring *vring_rsc;
        metal_phys_addr_t da;
        unsigned int num_descs, align;
        struct metal_io_region *io;
        void *va;
        size_t size;
        int ret;

        vring_rsc = &vdev_rsc->vring[i];
        notifyid = vring_rsc->notifyid;
        da = vring_rsc->da;
        num_descs = vring_rsc->num;
        align = vring_rsc->align;
        size = vring_size(num_descs, align);
        va = remoteproc_mmap(rproc, NULL, &da, size, 0, &io);
        if (!va) {
            goto err1;
        }
        ret = rproc_virtio_init_vring(vdev, i, notifyid, va, io, num_descs, align);
        if (ret) {
            goto err1;
        }
    }
    metal_mutex_release(&rproc->lock);
    return vdev;

err1:
    remoteproc_remove_virtio(rproc, vdev);
    metal_mutex_release(&rproc->lock);
    return NULL;
}

void remoteproc_remove_virtio(struct remoteproc *rproc, struct virtio_device *vdev)
{
    struct remoteproc_virtio *rpvdev;

    (void)rproc;
    metal_assert(vdev);

    if (vdev) {
        rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
        metal_list_del(&rpvdev->node);
        rproc_virtio_remove_vdev(&rpvdev->vdev);
    }
}

int remoteproc_get_notification(struct remoteproc *rproc, uint32_t notifyid)
{
    struct remoteproc_virtio *rpvdev;
    struct metal_list *node;
    int ret;

    if (!rproc) {
        return 0;
    }

    metal_list_for_each(&rproc->vdevs, node) {
        rpvdev = metal_container_of(node, struct remoteproc_virtio, node);
        ret = rproc_virtio_notified(&rpvdev->vdev, notifyid);
        if (ret) {
            return ret;
        }
    }

    return 0;
}
